﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;

namespace Co_location
{
    class BewellActivity
    {
        private static double KnownRatio = 0.75;
        static int ii = 1;
        static int jj = 1;
        static bool splitCom = false;
        static bool useCoLoc = false;
        static int preRun = 0;

        static Dictionary<string, int> gps2newLocID;
        static SortedDictionary<int, SortedDictionary<DateTime,Location>> user2Locs;

        static Dictionary<int, Dictionary<int, double>> coLocMatrix;
        static Dictionary<int, Dictionary<int, double>> WSCSMatrix;
        static Dictionary<int, Dictionary<int, int>> tf;
        
        static List<int> allusers = new List<int>() { 4, 6, 9, 10, 11, 13, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 36, 37, 40, 41 };
        static Dictionary<int, int> id2index = new Dictionary<int, int>() { { 4, 0 }, { 6, 1 }, { 9, 2 }, { 10, 3 }, { 11, 4 }, { 13, 5 }, { 18, 6 }, { 19, 7 }, 
        { 20, 8 }, { 21, 9 }, { 22, 10 }, { 23, 11 }, { 24, 12 }, { 25, 13 }, { 26, 14 }, { 32, 15 }, { 33, 16 }, { 34, 17 }, { 36, 18 }, { 37, 19 }, { 40, 20 }, { 41, 21 } };
        static HashSet<int> G1 = new HashSet<int>() { 4, 21, 33, 37 }, G2 = new HashSet<int>() { 26, 34, 36, 40, 41 },
            G3 = new HashSet<int>() { 6, 9, 13, 18, 20, 23, 32 }, G4 = new HashSet<int>() { 10, 11, 19, 22, 24, 25 };
        static Dictionary<int, HashSet<int>> Groups = new Dictionary<int, HashSet<int>>() { { 1, G1 }, { 2, G2 }, { 3, G3 }, { 4, G4 } };

        static Dictionary<int, List<int>> user2freaturenumber;
        static Dictionary<int, ComFeature> comFeatures;
        static double[] idf;

        static int maxLocID = 0;

        static double timeThreshold = 10, distanceThreshold = 0.0001;

        public static void SetUp(string[] args)
        {
            KnownRatio = double.Parse(args[0]);
            ii = int.Parse(args[1]);
            jj = int.Parse(args[2]);
            splitCom = bool.Parse(args[3]);
            if (args.Length > 4)
                useCoLoc = bool.Parse(args[4]);
            if (args.Length > 5)
                preRun = int.Parse(args[5]);
        }

        public static void Run()
        {
            if (preRun > 0)
                PreRun();
            else
            {
                outputKnown();

                if (ii == 1 && jj == 1)
                {
                    readCommunityFeature();
                    outputArff();
                    outputTruth();

                    readLocation();
                    calcWSCS();
                    calcCoLoc();

                    outputMatrix();
                    outputGraph();
                    outputCommuDetec();
                }
            }
        }

        private static void PreRun()
        {
            outputArff();
            outputTruth();

            readLocation();
            calcWSCS();
            calcCoLoc();
            outputMatrix();
            
            if (preRun == 1)
                outputGraph();
            
            #region outputKnownForReweight

            string tpath = System.IO.Directory.GetCurrentDirectory() + "\\Activity-Features.csv";
            StreamReader reader = new StreamReader(tpath);

            string path = System.IO.Directory.GetCurrentDirectory() + "\\activity-known.csv";
            StreamWriter writer = new StreamWriter(path);

            string line;

            int selected = 0;
            int all = 0;
            while ((line = reader.ReadLine()) != null)
            {
                if ((all + preRun) % 2 == 0)
                {
                    string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    writer.WriteLine(splits[0] + "," + splits[splits.Length - 1]);
                    selected++;
                }
                all++;
            }
            reader.Close();
            writer.Close();
            Console.WriteLine("Knonw Selected : " + selected);
            #endregion
        }

        private static void outputTruth()
        {
            user2freaturenumber = new Dictionary<int, List<int>>();

            string tpath = System.IO.Directory.GetCurrentDirectory() + "\\Activity-Features.csv";
            StreamReader reader = new StreamReader(tpath);

            StreamWriter writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\activity-truth.csv");

            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

                #region Feature Number
                int ui = splits[0].IndexOf('U'), fi = splits[0].IndexOf('F');
                int uid = int.Parse(splits[0].Substring(ui + 1, fi - ui - 1)), fid = int.Parse(splits[0].Substring(fi + 1));
                if (!user2freaturenumber.Keys.Contains(uid))
                    user2freaturenumber.Add(uid, new List<int>());
                user2freaturenumber[uid].Add(fid);
                #endregion

                #region output
                for (int i = 0; i < splits.Length - 1; i++)
                    writer.Write(splits[i] + ",");

                if (splitCom)
                {
                    ComFeature cf = comFeatures[uid];
                    for (int community = 1; community <= 4; community++)
                    {
                        if (Groups[community].Contains(uid))
                            outputComFeature(writer, cf, true);
                        else
                            outputComFeature(writer, cf, false);
                    }
                }
                writer.WriteLine(splits[splits.Length - 1]);                
                #endregion
            }
            reader.Close();
            writer.Close();
        }

        private static void outputComFeature(StreamWriter writer, ComFeature cf, bool belong)
        {
            if (belong)
                writer.Write(cf.Betweenness + "," + cf.Closeness + "," + cf.Eigenvector + "," + cf.PageRank + "," + cf.Clustering + ",");
            else
                writer.Write("0,0,0,0,0,");
        }

        private static void readLocation()
        {
            gps2newLocID = new Dictionary<string, int>();

            string path = "D:\\Pervasive\\Bewell\\location_dump.csv";

            StreamReader reader = new StreamReader(path);
            string line;

            user2Locs = new SortedDictionary<int, SortedDictionary<DateTime, Location>>();
            int count = 0;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                if (splits.Length != 7)
                    continue;
                

                Location loc = new Location(splits);
                if (loc.lat == 0 || loc.lon == 0 || !allusers.Contains(loc.uid))
                    continue;

                count++;

                #region calcLocid
                int latint = (int)Math.Floor(loc.lat / 0.0001);
                int lonint = (int)Math.Floor(loc.lon / 0.0001);
                string lockey = latint.ToString() + lonint.ToString();
                int tmplocid;
                if (!gps2newLocID.TryGetValue(lockey, out tmplocid))
                {
                    gps2newLocID.Add(lockey, maxLocID);
                    maxLocID++;
                }

                loc.newLocid = gps2newLocID[lockey];
                #endregion

                SortedDictionary<DateTime, Location> locs;

                if (!user2Locs.TryGetValue(loc.uid, out locs))
                {
                    locs = new SortedDictionary<DateTime, Location>();
                    user2Locs.Add(loc.uid, locs);
                }
                if (!locs.Keys.Contains(loc.ts))
                {
                    locs.Add(loc.ts, loc);
                    user2Locs[loc.uid] = locs;
                }
            }
            reader.Close();

            Console.WriteLine("Loc Number: " + count);
        }

        private static void readCommunityFeature()
        {
            comFeatures = new Dictionary<int, ComFeature>();
            string cPath = System.IO.Directory.GetCurrentDirectory() + "\\CommunityFeatures.csv";
            StreamReader reader = new StreamReader(cPath);
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                ComFeature cf = new ComFeature(splits);
                comFeatures.Add(cf.id, cf);
            }
            reader.Close();
        }
        
        private static void outputArff()
        {
            string path = System.IO.Directory.GetCurrentDirectory() + "\\activity-schema.arff";
            StreamWriter writer = new StreamWriter(path);

            writer.WriteLine("@nodetype FeatL");
            writer.WriteLine("@attribute NAME KEY");
            
            for (int i = 1; i <= 50; i++)
            {
                writer.WriteLine("@attribute Feat" + i + " CONTINUOUS");
            }

            if (splitCom)
                for (int i = 1; i <= 4; i++)
                {
                    writer.WriteLine(string.Format("@attribute Between{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Close{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute EigenVector{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Pagerank{0} CONTINUOUS", i));
                    writer.WriteLine(string.Format("@attribute Clustering{0} CONTINUOUS", i));
                }
            writer.WriteLine("@attribute Label CATEGORICAL");
            writer.WriteLine("@nodedata activity-truth.csv");

            writer.WriteLine();

            writer.WriteLine("@edgetype Linked FeatL FeatL");
            writer.WriteLine("@Reversible");
            writer.WriteLine("@edgedata activity-link.rn");

            writer.Close();
        }
        
        private static void outputMatrix()
        {
            StreamWriter writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\activity-Matrix.csv");
            foreach (int user in allusers)
                writer.Write(",U" + user);
            writer.WriteLine();

            SortedSet<double> edgeWeights = new SortedSet<double>();
            foreach (int userA in allusers)
            {
                foreach (int userB in allusers)
                {
                    edgeWeights.Add(WSCSMatrix[userA][userB]);
                }
            }
            double median = edgeWeights.ElementAt(edgeWeights.Count * 1 / 2);
            foreach (int user in allusers)
            {
                writer.Write("U" + user);
                foreach (int userb in allusers)
                {
                    if (WSCSMatrix[user][userb] > median)
                        writer.Write("," + WSCSMatrix[user][userb].ToString("0.0000"));
                    else
                        writer.Write(",0");
                }

                writer.WriteLine();
            }
            writer.Close();
            
            //foreach (int user in allusers)
            //{
            //    foreach (int userb in allusers)
            //        writer.Write(coLocMatrix[user][userb].ToString("0.0000") + ",");

            //    writer.WriteLine();
            //}

            //writer.WriteLine();
            //for (int loc = 0; loc < maxLocID; loc++)
            //    writer.Write(idf[loc].ToString("0.000") + ",");

            //writer.WriteLine("\n");
            //foreach (int user in allusers)
            //{
            //    for (int loc = 0; loc < maxLocID; loc++)
            //        writer.Write(tf[user][loc] + ",");
            //    writer.WriteLine();
            //}
            //writer.Close();
        }

        private static void outputKnown()
        {
            string tpath = System.IO.Directory.GetCurrentDirectory() + "\\Activity-Features.csv";
            StreamReader reader = new StreamReader(tpath);
           
            string path = System.IO.Directory.GetCurrentDirectory() + "\\activity-known.csv";
            StreamWriter writer = new StreamWriter(path);

            string line;

            Random rand = new Random();
            int selected = 0;
            while ((line = reader.ReadLine()) != null)
            {
                if (rand.NextDouble() <= KnownRatio)
                {
                    string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    writer.WriteLine(splits[0] + "," + splits[splits.Length - 1]);
                    selected++;
                }
            }
            reader.Close();
            writer.Close();
            Console.WriteLine("Knonw Selected : " + selected);
        }

        private static void outputGraph()
        {
            #region oldversion using matlab matrix
            //int[] v = new int[22];
            //StreamReader vReader = new StreamReader("D:\\Work\\Pervasive\\Bewell\\Activity\\activity-vector.txt");
            //string line = vReader.ReadLine();
            //string[] splits = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            //if (splits.Length != 22)
            //    throw new Exception("Length!!! " + line);

            //for (int i = 0; i < 22; i++)
            //    v[i] = int.Parse(splits[i]);
            //vReader.Close();

            //double[] a2 = new double[22 * 22];


            //StreamReader mReader = new StreamReader("D:\\Work\\Pervasive\\Bewell\\Activity\\bewell-matrix.txt");
            //for (int i = 0; i < 22; i++)
            //{
            //    line = mReader.ReadLine();
            //    splits = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            //    if (splits.Length != 22)
            //        throw new Exception("Length!!! " + line);
            //    for (int j = 0; j < 22; j++)
            //        a2[i * 22 + j] = double.Parse(splits[j]);
            //}

            //mReader.ReadLine();

            //double[] a3 = new double[22 * 22];
            //for (int i = 0; i < 22; i++)
            //{
            //    line = mReader.ReadLine();
            //    splits = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            //    if (splits.Length != 22)
            //        throw new Exception("Length!!! " + line);
            //    for (int j = 0; j < 22; j++)
            //        a3[i * 22 + j] = double.Parse(splits[j]);
            //}

            //mReader.Close();

            //string u1, u2;
            //StreamWriter lWriter = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\activity-link.rn");
            //for (int i = 1; i <= 22; i++)
            //    for (int j = 1; j <= 22; j++)
            //    {
            //        int m = v[i - 1];
            //        int n = v[j - 1];
            //        double mvalue = a2[i * 22 - 22 + j - 1];
            //        if (mvalue < 0.89) continue;
            //        if (mvalue >= 1) continue;

            //        for (int p = 1; p <= m; p++)
            //        {
            //            u1 = "U" + allusers[i - 1] + "F" + p;
            //            for (int q = 1; q <= n; q++)
            //            {
            //                u2 = "U" + allusers[j - 1] + "F" + q;
            //                if (i == j && p > q)
            //                    continue;

            //                lWriter.WriteLine(u1 + "," + u2 + "," + mvalue);
            //            }
            //        }
            //    }

            //lWriter.Close();
            #endregion

            StreamWriter writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\activity-link.rn");

            int edge = 0;
            foreach (int userA in allusers)
            {
                foreach (int userB in allusers)
                {
                    if (userA > userB) continue;

                    if (useCoLoc)
                    {
                        if (coLocMatrix[userA][userB] < 0.2 || coLocMatrix[userA][userB] >= 1) continue;
                    }
                    else
                    {
                        if (WSCSMatrix[userA][userB] < 0.05|| WSCSMatrix[userA][userB] >= 1) continue;
                    }
                    
                    List<int> FeatureA = user2freaturenumber[userA];
                    List<int> FeatureB = user2freaturenumber[userB];

                    for (int i = 0; i < FeatureA.Count; i++)
                    {
                        for (int j = 0; j < FeatureB.Count; j++)
                        {
                            if (userA == userB && i >= j)
                                continue;

                            if (useCoLoc)
                                writer.WriteLine(getName(userA, FeatureA[i]) + "," + getName(userB, FeatureB[j]) + "," + coLocMatrix[userA][userB].ToString("0.000"));
                            else
                                writer.WriteLine(getName(userA, FeatureA[i]) + "," + getName(userB, FeatureB[j]) + "," + WSCSMatrix[userA][userB].ToString("0.000"));
                            edge++;
                        }
                    }
                }
            }

            Console.WriteLine("Edge Number : " + edge);
            writer.Close();
        }

        private static void outputCommuDetec()
        {
            SortedSet<double> edgeWeights = new SortedSet<double>();
            foreach (int userA in allusers)
            {
                foreach (int userB in allusers)
                {
                    edgeWeights.Add(WSCSMatrix[userA][userB]);
                }
            }
            double median = edgeWeights.ElementAt(edgeWeights.Count * 1 / 2);

            string gPath = System.IO.Directory.GetCurrentDirectory() + "\\activity-graph.txt";
            StreamWriter writer = new StreamWriter(gPath);
            foreach(int userA in allusers)
                foreach (int userB in allusers)
                {
                    if (WSCSMatrix[userA][userB] < median)
                        continue;

                    writer.WriteLine(id2index[userA] + " " + id2index[userB]);
                }
            writer.Close();

            string wPath = System.IO.Directory.GetCurrentDirectory() + "\\activity-weight.weights";
            writer = new StreamWriter(wPath);
            foreach (int userA in allusers)
                foreach (int userB in allusers)
                {
                    if (WSCSMatrix[userA][userB] < median)
                        continue;

                    writer.WriteLine(id2index[userA] + " " + id2index[userB] + " " + Math.Sqrt(WSCSMatrix[userA][userB]).ToString("0.0000"));
                }
            writer.Close();

            //outputCommunity(G1, "G1");
            //outputCommunity(G2, "G2");
            //outputCommunity(G3, "G3");
            //outputCommunity(G4, "G4");
        }

        private static void outputCommunity(HashSet<int> group, string groupname)
        {
            StreamWriter writer = new StreamWriter(System.IO.Directory.GetCurrentDirectory() + "\\" + groupname + ".csv");
            foreach (int user in group)
                writer.Write(",U" + user);
            writer.WriteLine();

            foreach (int i in group)
            {
                writer.Write("U" + i);
                foreach (int j in group)
                    writer.Write("," + WSCSMatrix[i][j].ToString("0.000"));

                    writer.WriteLine();
             
            }
            writer.Close();
        }

        private static string getName(int u, int f)
        {
            return "U" + u + "F" + f;
        }

        private static void calcCoLoc()
        {
            coLocMatrix = new Dictionary<int, Dictionary<int, double>>();
            foreach (int userA in allusers)
            {
                coLocMatrix.Add(userA, new Dictionary<int, double>());
                foreach (int userB in allusers)
                {
                    coLocMatrix[userA].Add(userB, 0);

                    SortedDictionary<DateTime, Location>.Enumerator locA = user2Locs[userA].GetEnumerator();
                    SortedDictionary<DateTime, Location>.Enumerator locB = user2Locs[userB].GetEnumerator();

                    bool moveA = locA.MoveNext(), moveB = locB.MoveNext();
                    double coLocNum = 0;
                    while (moveA && moveB)
                    {
                        double interval = (locA.Current.Key - locB.Current.Key).TotalMinutes;

                        if (Math.Abs(interval) < timeThreshold)
                        {
                            if (CalcDistance(locA.Current.Value, locB.Current.Value) < distanceThreshold)
                                coLocNum++;

                            moveA = locA.MoveNext();
                            moveB = locB.MoveNext();
                        }
                        else if (interval > 0)
                            moveB = locB.MoveNext();
                        else
                            moveA = locA.MoveNext();
                    }
                    coLocMatrix[userA][userB] = coLocNum * 2 / (user2Locs[userA].Count + user2Locs[userB].Count);
                }
            }
        }

        private static double CalcDistance(Location l1, Location l2)
        {
            return Math.Sqrt((l1.lat - l2.lat) * (l1.lat - l2.lat) + (l1.lon - l2.lon) * (l1.lon - l2.lon));
        }

        private static void calcWSCS()
        {
            #region calc tf
            tf = new Dictionary<int, Dictionary<int, int>>();
            foreach (int user in allusers)
            {
                tf.Add(user,new Dictionary<int,int>());
                for (int i = 0; i < maxLocID; i++)
                    tf[user].Add(i, 0);

                foreach (Location loc in user2Locs[user].Values)
                {
                    tf[user][loc.newLocid]++;
                }
            }
            #endregion

            #region calc idf ( l )
            idf = new double[maxLocID];
            for (int loc = 0; loc < maxLocID; loc++)
            {
                double div = 0;
                foreach (int user in allusers)
                    if (tf[user][loc]!=0)
                        div++;
                idf[loc] = Math.Log((double)allusers.Count / div);
            }

            #endregion

            #region calc matrix
            WSCSMatrix = new Dictionary<int, Dictionary<int, double>>();
            foreach (int usera in allusers)
            {
                List<double> vectora = new List<double>();
                for (int i = 0; i < maxLocID; i++)
                    vectora.Add(tf[usera][i] * idf[i]);

                WSCSMatrix.Add(usera, new Dictionary<int, double>());
                foreach (int userb in allusers)
                {
                    List<double> vectorb = new List<double>();
                    for (int i = 0; i < maxLocID; i++)
                        vectorb.Add(tf[userb][i] * idf[i]);

                    WSCSMatrix[usera].Add(userb, cosineSimilarity(vectora, vectorb));
                }
                WSCSMatrix[usera][usera] = 0;
            }
            #endregion
        }

        private static double cosineSimilarity(List<double> veca, List<double> vecb)
        {
            double scalea = 0, scaleb = 0;
            double crossproduct = 0;
            for (int i = 0; i < veca.Count; i++)
            {
                crossproduct += veca[i] * vecb[i];

                scalea += veca[i] * veca[i];
                scaleb += vecb[i] * vecb[i];
            }
            //Console.WriteLine(crossproduct + " " + veca[0] + " " + vecb[0]);
            return crossproduct / (Math.Sqrt(scalea) * Math.Sqrt(scaleb));
        }
        
        public static void MatchUp()
        {
            for (int k = 3; k <= 3; k++)
            {
                if (!(k == 3 || k == 5 || (k >= 14 && k <= 18) || k == 20))
                    continue;

                StreamWriter writer = new StreamWriter("D:\\Pervasive\\Bewell\\Sleep\\MatchUp.txt", true);

                string noidpath = "D:\\Pervasive\\Bewell\\Match\\activity-match" + k + ".csv";
                StreamReader noidreader = new StreamReader(noidpath);
                string line;

                List<List<double>> rows = new List<List<double>>();

                while ((line = noidreader.ReadLine()) != null)
                {
                    string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    if (splits.Length != 52)
                        throw new Exception("match file exceptioin " + splits.Length);

                    List<double> values = new List<double>();
                    for (int i = 26; i < 35; i++)
                    {
                        values.Add((double.Parse(splits[i])));
                    }
                    rows.Add(values);
                }
                noidreader.Close();

                int matched = 0;

                string idpath = "D:\\Pervasive\\Bewell\\Match";
                IEnumerator<string> idfiles = System.IO.Directory.EnumerateFiles(idpath).GetEnumerator();

                Console.WriteLine("id : " + k + " : " + rows.Count);

                while (idfiles.MoveNext())
                {
                    string idfile = idfiles.Current;

                    HashSet<string> set = getMatchedSet();
                    string filename = idfile.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)[5];
                    if (set.Contains(filename) || filename.Contains("match"))
                        continue;

                    StreamReader idreader = new StreamReader(idfile);
                    int id = -1;
                    List<List<double>> rows2 = new List<List<double>>();

                    while ((line = idreader.ReadLine()) != null)
                    {
                        string[] splits = line.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

                        int tmp;
                        if (splits.Length < 1)
                            continue;

                        if (!int.TryParse(splits[0], out tmp))
                            continue;

                        id = int.Parse(splits[1]);

                        if (int.Parse(splits[3]) != 3)
                            continue;

                        List<double> values = new List<double>();
                        for (int i = 4; i < 13; i++)
                        {
                            values.Add((double.Parse(splits[i])));
                        }
                        rows2.Add(values);
                    }
                    idreader.Close();
                    //writer.WriteLine(idfile.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)[5] + " " + rows2.Count);

                    Console.WriteLine(idfile.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)[5] + " " + rows2.Count);

                    for (int i = 0; i < rows2.Count; i++)
                    {
                        for (int j = 0; j < rows.Count; j++)
                        {
                            if (Compare(rows2[i], rows[j]))
                            {
                                Console.Write(rows[j][0]);
                                Console.WriteLine(" Matched: " + id + " i: " + i + " with " + k + " j : " + j);
                                matched++;
                                if (matched > 10)
                                    break;
                            }
                        }
                        if (matched > 10)
                            break;
                    }
                    if (matched > 10)
                    {
                        Console.WriteLine("Match " + id + " " + k);
                        break;
                    }
                }
                writer.Close();
            }

        }

        private static bool Compare(List<double> vs1, List<double> vs2)
        {
            for (int i = 0; i < Math.Min(vs1.Count, vs2.Count); i++)
            {
                if (Math.Abs(vs1[i] - vs2[i]) >= 0.0001)
                    return false;
            }
            return true;
        }

        private static HashSet<string> getMatchedSet()
        {
            HashSet<string> set = new HashSet<string>();
            set.Add("feat_4.csv");
            set.Add("feat_6.csv");
            set.Add("feat_10.csv");
            set.Add("feat_13.csv");
            set.Add("feat_18.csv");
            set.Add("feat_19.csv");
            set.Add("feat_20.csv");
            set.Add("feat_21.csv");
            set.Add("feat_22.csv");
            set.Add("feat_23.csv");
            set.Add("feat_24.csv");
            set.Add("feat_36.csv");
            set.Add("feat_40.csv");
            set.Add("feat_41.csv");
            return set;
        }

        private static void outputforCheck(List<double> r1, List<double> r2)
        {
            Console.Write("R1 : ");
            for (int i = 0; i < r1.Count; i++)
                Console.Write(r1[i].ToString("0.00000") + " ");
            Console.WriteLine();

            Console.Write("R2 : ");
            for (int i = 0; i < r2.Count; i++)
                Console.Write(r2[i].ToString("0.00000") + " ");
            Console.WriteLine();
        }

        struct Location
        {
            public int lid;
            public int uid;
            public int type;

            public double lat;
            public double lon;

            public DateTime ts;

            public int newLocid;

            public Location(string[] splits)
            {
                lid = int.Parse(splits[0]);
                uid = int.Parse(splits[1]);                

                type = int.Parse(splits[2]);

                lat = double.Parse(splits[3]);
                lon = double.Parse(splits[4]);

                string[] dt = splits[5].Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                string[] date = dt[0].Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
                string[] time = dt[1].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                try
                {
                    string[] seconds = time[2].Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
                    int millis;
                    if (seconds.Length == 2)
                        millis = int.Parse(seconds[1]);
                    else
                        millis = 0;

                    ts = new DateTime(int.Parse(date[0]), int.Parse(date[1]), int.Parse(date[2]), int.Parse(time[0]), int.Parse(time[1]), int.Parse(seconds[0]), millis);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    Console.WriteLine(splits[5]);
                    throw ex;
                }

                newLocid = -1;
            }
        }
        
        struct ComFeature
        {
            public int id;
            public double Betweenness;
            public double Closeness;
            public double Eigenvector;
            public double PageRank;
            public double Clustering;

            public ComFeature(string[] splits)
            {
                id = int.Parse(splits[0]);
                Betweenness = double.Parse(splits[1]);
                Closeness = double.Parse(splits[2]);
                Eigenvector = double.Parse(splits[3]);
                PageRank = double.Parse(splits[4]);
                Clustering = double.Parse(splits[5]);
            }
        }

        public static void ReoutputFeature()
        {
            string tpath = System.IO.Directory.GetCurrentDirectory() + "\\Activity-Features.csv";
            StreamReader reader = new StreamReader(tpath);

            string path = System.IO.Directory.GetCurrentDirectory() + "\\Re-Features.csv";
            StreamWriter writer = new StreamWriter(path);

            string line;
            double[] ratio = new double[7] { 0.055 * 0, 0.121 * 1.5, 0.027 * 1.5, 0.036 * 0, 0.030 * 0.1, 0.090 * 1.5, 0.129 * 1.5 };
            Random rand = new Random();
            int selected = 0;
            
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                int label = int.Parse(splits[splits.Length - 1]);
                double pos = (ratio[label - 1]);
                if (splits[0].Contains("U6") || splits[0].Contains("U9"))
                    pos *= 3;
                if (splits[0].Contains("U13") || splits[0].Contains("U26")  )
                    pos *= 10;
                if (splits[0].Contains("U21") || splits[0].Contains("U20"))
                    pos *= 10;
                if (rand.NextDouble() < pos || splits[0].Contains("U33"))
                    writer.WriteLine(line);
                //writer.WriteLine(splits[0] + "," + splits[splits.Length - 1]);
                selected++;
            }
            reader.Close();
            writer.Close();
            Console.WriteLine("Knonw Selected : " + selected);
        }

        #region Label Correlation

        static List<int> allLabels;
        static Dictionary<int, Dictionary<int, double>> cosDis;
        static Dictionary<int, Dictionary<int, double>> eucLid;

        static Dictionary<int, Dictionary<int, int>> label_tf;

        static Dictionary<int, double> label_idf;
        static Dictionary<int, Dictionary<int, double>> label_wscsmatrix;

        public static void OutputLabelCor()
        {
            readLocation();
            calcWSCS();
            calcCoLoc();

            getLabelTF();
            calcLabelWSCS();
            calcCosDis();
            calcEucLid();

            General.OutputLabelCor(WSCSMatrix, label_wscsmatrix);
            General.OutputFivePercent(label_wscsmatrix);
            General.OutputAverage(WSCSMatrix, label_wscsmatrix);
            General.OutputAvgEuc(eucLid, null);
        }

        private static void getLabelTF()
        {
            allLabels = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
  
            label_tf = new Dictionary<int, Dictionary<int, int>>();
            foreach (int user in allusers)
            {
                label_tf.Add(user, new Dictionary<int, int>());
                foreach (int label in allLabels)
                    label_tf[user].Add(label, 0);
            }
            
            StreamReader reader = new StreamReader(System.IO.Directory.GetCurrentDirectory() + "\\Activity-Features.csv");
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                string[] splits = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                int ui = splits[0].IndexOf('U'), fi = splits[0].IndexOf('F');
                int uid = int.Parse(splits[0].Substring(ui + 1, fi - ui - 1));
                int label = int.Parse(splits[splits.Length-1]);
                try
                {
                    label_tf[uid][label]++;
                }
                catch (Exception)
                {
                    Console.WriteLine(uid + " " + label);
                    throw;
                }
            }            
            reader.Close();            
        }

        private static void calcCosDis()
        {
            cosDis = new Dictionary<int, Dictionary<int, double>>();

            foreach (int userA in label_tf.Keys)
            {
                cosDis.Add(userA, new Dictionary<int, double>());

                foreach (int userB in label_tf.Keys)
                {
                    double dotProduct = 0;
                    double magA = 0, magB = 0;
                    foreach (var pair in label_tf[userA])
                    {
                        dotProduct += pair.Value * label_tf[userB][pair.Key];
                        magA += pair.Value * pair.Value;
                        magB += label_tf[userB][pair.Key] * label_tf[userB][pair.Key];
                    }
                    cosDis[userA].Add(userB, dotProduct / Math.Sqrt(magA * magB));
                }
            }
        }

        private static void calcLabelWSCS()
        {
            #region idf

            int maxUserNum = allusers.Count;
            label_idf = new Dictionary<int, double>();

            foreach (int l in allLabels)
            {
                double div = 0;
                foreach (int user in label_tf.Keys)
                {
                    if (label_tf[user][l] != 0)
                        div++;
                }
                label_idf.Add(l, Math.Log((double)maxUserNum / div));
            }
            #endregion

            #region label wscs

            label_wscsmatrix = new Dictionary<int, Dictionary<int, double>>();
            foreach (int usera in label_tf.Keys)
            {
                List<double> vectora = new List<double>();
                foreach (int i in allLabels)
                    vectora.Add(label_tf[usera][i] * label_idf[i]);

                label_wscsmatrix.Add(usera, new Dictionary<int, double>());
                foreach (int userb in label_tf.Keys)
                {
                    List<double> vectorb = new List<double>();
                    foreach (int i in allLabels)
                        vectorb.Add(label_tf[userb][i] * label_idf[i]);

                    label_wscsmatrix[usera].Add(userb, cosineSimilarity(vectora, vectorb));
                }
                label_wscsmatrix[usera][usera] = 0;
            }
            #endregion 
        }

        private static void calcEucLid()
        {
            eucLid = new Dictionary<int, Dictionary<int, double>>();

            foreach (int userA in label_tf.Keys)
            {
                eucLid.Add(userA, new Dictionary<int, double>());

                foreach (int userB in label_tf.Keys)
                {
                    double sum = 0;
                    foreach (var pair in label_tf[userA])
                    {
                        sum += (pair.Value - label_tf[userB][pair.Key]) * (pair.Value - label_tf[userB][pair.Key]);
                    }
                    eucLid[userA].Add(userB, Math.Sqrt(sum));
                }
            }
        }
        #endregion
    }
}
